package com.photons.timesync.viewmodel

import android.app.ProgressDialog
import android.content.Context
import android.util.Log
import androidx.databinding.Bindable
import androidx.databinding.Observable
import androidx.databinding.PropertyChangeRegistry
import androidx.databinding.library.baseAdapters.BR
import androidx.hilt.lifecycle.ViewModelInject
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.photons.timesync.App
import com.photons.timesync.socket.UDPServer
import com.photons.timesync.socket.UDPSocket
import com.photons.timesync.utils.NetUtils
import kotlinx.coroutines.launch
import java.io.*
import java.net.DatagramPacket
import java.util.*
import kotlin.concurrent.fixedRateTimer
import kotlin.math.abs

class TimeSyncViewModel @ViewModelInject constructor(
    private val udpSocket: UDPSocket,
    udpServer: UDPServer
) : ViewModel(), Observable {
    val clientMsg = MutableLiveData<String>()
    val serverMsg = MutableLiveData<String>()
    val curTime = MutableLiveData<String>()
    private var timer: Timer
    private var timeDiff = 0L
    private val samples = DoubleArray(SAMPLE_COUNT)

    companion object {
        const val SERVER_PORT = 3000
        const val SAMPLE_COUNT = 200
        const val TAG = "TimeSyncViewModel"
    }

    init {
        udpServer.start(SERVER_PORT, object : UDPServer.ResponseHandler {
            override fun genResponse(): ByteArray {
                return packetTime(System.currentTimeMillis())
            }

            override fun onRequest(data: DatagramPacket?) {
                val peerTime = unpackTime(data)
                val myTime = System.currentTimeMillis()
                serverMsg.postValue("收到消息: \npeer=\t$peerTime\nmine=\t$myTime")
            }
        })

        timer = fixedRateTimer("timer", false, 0, 30) {
            val cur = System.currentTimeMillis()
            val syncedWithPeer = cur - timeDiff
            curTime.postValue("本机：$cur\n对端：$syncedWithPeer")
        }
    }

    private fun packetTime(ms: Long): ByteArray {
        val baos = ByteArrayOutputStream()
        val dos = DataOutputStream(baos)
        try {
            dos.writeLong(ms)
            return baos.toByteArray()
        } catch (e: IOException) {
            e.printStackTrace()
        } finally {
            dos.close()
            baos.close()
        }

        return ByteArray(0)
    }

    private fun unpackTime(packet: DatagramPacket?): Long {
        if (packet == null)
            return -1;

        val dis = DataInputStream(ByteArrayInputStream(packet.data, packet.offset, packet.length))
        try {
            return dis.readLong()
        } catch (e: IOException) {
            e.printStackTrace()
        } finally {
            dis.close()
        }

        return 0
    }


    private var peerIP = NetUtils.getPeerIP()

    @Bindable
    fun getPeerIP(): String {
        return peerIP
    }

    fun setPeerIP(value: String) {
        if (peerIP != value) {
            peerIP = value

            NetUtils.savePeerIP(value)

            notifyPropertyChanged(BR.peerIP)
        }
    }

    private suspend fun syncTime(): Double {
        val t0 = System.currentTimeMillis()
        val reply = udpSocket.sendMessageWithReply(packetTime(t0), peerIP, SERVER_PORT)
        val t1 = System.currentTimeMillis()
        val peerTime = unpackTime(reply)

        return if (peerTime > 0) {
            val tn = (t1 - t0) / 2.0
            Log.d(TAG, "tn=$tn, t0=$t0, t1=$t1, peerTime=$peerTime")
            t1 - peerTime - tn
        } else {
            0.0
        }
    }

    fun send(context: Context) {
        val progressDialog = ProgressDialog.show(context, "稍安勿躁", "请保持对端在线，同步中...")
        Arrays.fill(samples, 0.0)
        viewModelScope.launch {

            for (i in samples.indices) {
                samples[i] = syncTime()
            }

            // 排序
            samples.sort()
            for (i in samples.indices) {
                Log.d(TAG, "sync $i of ${samples[i]}")
            }

            var total = 0.0
            var count = 0L

            // 只取中间一半数据
            for (i in (samples.size/4)..(samples.size*3/4)) {
                if (samples[i] == 0.0) {
                    continue
                }

                total += samples[i]
                count++
            }
            timeDiff = (total / count).toLong()

            when {
                timeDiff > 0L -> {
                    clientMsg.postValue("本机比对端快 ${abs(timeDiff)} ms")
                }
                timeDiff < 0L -> {
                    clientMsg.postValue("本机比对端慢 ${abs(timeDiff)} ms")
                }
                timeDiff == 0L -> {
                    clientMsg.postValue("未同步")
                }
            }

            progressDialog.dismiss()
        }
    }


    // 为了在继承ViewModel后，仍然可以被观察（之前要继承BaseObservable），需要自己参考BaseObservable
    // 实现Observable里面的方法
    private val callbacks: PropertyChangeRegistry = PropertyChangeRegistry()

    override fun removeOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
        callbacks.add(callback)
    }

    override fun addOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
        callbacks.remove(callback)
    }

    private fun notifyPropertyChanged(fieldId: Int) {
        callbacks.notifyCallbacks(this, fieldId, null)
    }
}